/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *   gbx-emu - cpu.cpp                                                     *
 *   gbx-emu homepage: http://code.google.com/p/gbx-emu/                   *
 *   Copyright (C) 2009 olejl77@gmail.com                                  *
 *                                                                         *
 *   This program is free software: you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation, either version 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "common.h"
#include "cpu.h"
#include "gameboy.h"
#include "memory.h"
#include "screen.h"
#include "debugger.h"

/**
 * \class Cpu
 * Gameboy Cpu
**/

/**
 * \fn Cpu::Cpu(Gameboy *ptr)
 * Contructor.
 * \param *ptr  Pointer to the Gameboy / parent class which created the memory class.
 * required for communicating with other classes.
**/
Cpu::Cpu(Gameboy *ptr)
{
    _gb = ptr;

    // allocate memory
    _c = new u8[0x100];
    _c2 = new u8[0x100];
    _opcode_cnt = new u32[0x100];
    _opcode_cnt_2 = new u32[0x100];

    if ((_c == NULL) || (_c2 == NULL) || (_opcode_cnt == NULL) || (_opcode_cnt_2 == NULL)) {
        throw "[Error] Failed to initialize memory";
    }

    // initialize the cycles table
    memset(_c, 0x04, sizeof(_c));
    _c[0x01]=12;_c[0x02]= 8;_c[0x03]= 8;_c[0x06]= 8;_c[0x08]=20;_c[0x09]= 8;_c[0x0A]= 8;_c[0x0B]= 8;
    _c[0x0E]= 8;_c[0x11]=12;_c[0x12]= 8;_c[0x13]= 8;_c[0x16]= 8;_c[0x18]= 8;_c[0x19]= 8;_c[0x1A]= 8;
    _c[0x1B]= 8;_c[0x1E]= 8;_c[0x20]= 8;_c[0x21]=12;_c[0x22]= 8;_c[0x23]= 8;_c[0x26]= 8;_c[0x28]= 8;
    _c[0x29]= 8;_c[0x2A]= 8;_c[0x2B]= 8;_c[0x2E]= 8;_c[0x30]= 8;_c[0x31]=12;_c[0x32]= 8;_c[0x33]= 8;
    _c[0x34]=12;_c[0x35]=12;_c[0x36]=12;_c[0x38]= 8;_c[0x39]= 8;_c[0x3A]= 8;_c[0x3B]= 8;_c[0x3E]= 8;
    _c[0x46]= 8;_c[0x4E]= 8;_c[0x56]= 8;_c[0x5E]= 8;_c[0x66]= 8;_c[0x6E]= 8;_c[0x70]= 8;_c[0x71]= 8;
    _c[0x72]= 8;_c[0x73]= 8;_c[0x74]= 8;_c[0x75]= 8;_c[0x77]= 8;_c[0x7E]= 8;_c[0x86]= 8;_c[0x8E]= 8;
    _c[0x96]= 8;_c[0x9E]= 8;_c[0xA6]= 8;_c[0xAE]= 8;_c[0xB6]= 8;_c[0xBE]= 8;_c[0xC0]= 8;_c[0xC1]=12;
    _c[0xC2]=12;_c[0xC3]=12;_c[0xC4]=12;_c[0xC5]=16;_c[0xC6]= 8;_c[0xC7]=32;_c[0xC8]= 8;_c[0xC9]= 8;
    _c[0xCA]=12;_c[0xCC]=12;_c[0xCD]=12;_c[0xCE]= 8;_c[0xCF]=32;_c[0xD0]= 8;_c[0xD1]=12;_c[0xD2]=12;
    _c[0xD4]=12;_c[0xD5]=16;_c[0xD6]= 8;_c[0xD7]=32;_c[0xD8]= 8;_c[0xD9]= 8;_c[0xDA]=12;_c[0xDC]=12;
    _c[0xDF]=32;_c[0xE0]=12;_c[0xE1]=12;_c[0xE2]= 8;_c[0xE5]=16;_c[0xE6]= 8;_c[0xE7]=32;_c[0xE8]=16;
    _c[0xEA]=16;_c[0xEE]= 8;_c[0xEF]=32;_c[0xF0]=12;_c[0xF1]=12;_c[0xF2]= 8;_c[0xF5]=16;_c[0xF6]= 8;
    _c[0xF7]=32;_c[0xF8]=12;_c[0xF9]= 8;_c[0xFA]=16;_c[0xFE]= 8;_c[0xFF]=32;

    // Initialize cycles for the special opcode 0xcb
    memset(_c2, 0x08, sizeof(_c2));
    _c2[0x06]=16;_c2[0x0e]=16;_c2[0x16]=16;_c2[0x1e]=16;_c2[0x26]=16;_c2[0x2e]=16;_c2[0x36]=16;_c2[0x3e]=16;
    _c2[0x46]=16;_c2[0x4e]=16;_c2[0x56]=16;_c2[0x5e]=16;_c2[0x66]=16;_c2[0x6e]=16;_c2[0x76]=16;_c2[0x7e]=16;
    _c2[0x86]=16;_c2[0x8e]=16;_c2[0x96]=16;_c2[0x9e]=16;_c2[0xa6]=16;_c2[0xae]=16;_c2[0xb6]=16;_c2[0xbe]=16;
    _c2[0xc6]=16;_c2[0xce]=16;_c2[0xd6]=16;_c2[0xde]=16;_c2[0xe6]=16;_c2[0xee]=16;_c2[0xf6]=16;_c2[0xfe]=16;

    memset(_opcode_cnt, 0x00, sizeof(_opcode_cnt));
    memset(_opcode_cnt_2, 0x00, sizeof(_opcode_cnt_2));

    _debuggerEnabled = false;
}

/**
 * \fn Cpu::~Cpu()
 * Destructor.
 * Deleting dynamically allocated memory.
 * Sets all pointers to NULL to avoid them being used.
**/
Cpu::~Cpu()
{
    delete [] _c;
    delete [] _c2;
    delete [] _opcode_cnt;
    delete [] _opcode_cnt_2;

    _c = NULL;
    _c2 = NULL;
    _opcode_cnt = NULL;
    _opcode_cnt_2 = NULL;

    _gb = NULL;
    _log = NULL;
    _mem = NULL;
    _scr = NULL;
    _dbg = NULL;
}

/**
 * \fn void Cpu::resetCpu()
 * Cpu Reset.
 * Sets up registers and IO registers according to the real Gameboy.
**/
void Cpu::resetCpu()
{
    _A      = 0x01;
    _Z      = 1;
    _N      = 0;
    _H      = 1;
    _C      = 1;
    _BC.w   = 0x0013;
    _DE.w   = 0x00D8;
    _HL.w   = 0x014d;
    m_SP     = 0xfffe;
    _mem->write(TIMA,  0x00);
    _mem->write(TMA,   0x00);
    _mem->write(TAC,   0x00);
    _mem->write(NR10,  0x80);
    _mem->write(NR11,  0xbf);
    _mem->write(NR12,  0xf3);
    _mem->write(NR14,  0xbf);
    _mem->write(NR21,  0x3f);
    _mem->write(NR22,  0x00);
    _mem->write(NR24,  0xbf);
    _mem->write(NR30,  0x7f);
    _mem->write(NR31,  0xff);
    _mem->write(NR32,  0x9f);
    _mem->write(NR33,  0xbf);
    _mem->write(NR41,  0xff);
    _mem->write(NR42,  0x00);
    _mem->write(NR43,  0x00);
    _mem->write(NR30,  0xbf);
    _mem->write(NR50,  0x77);
    _mem->write(NR51,  0xf3);
    _mem->write(NR52,  0xf1); // GB
    // _mem->write(NR52,  0xf0); // SGB
    _mem->write(LCDC,  0x91);
    _mem->write(SCY,   0x00);
    _mem->write(SCX,   0x00);
    _mem->write(LYC,   0x00);
    _mem->write(BGP,   0xfc);
    _mem->write(OBP0,  0xff);
    _mem->write(OBP1,  0xff);
    _mem->write(WY,    0x00);
    _mem->write(WX,    0x00);
    _mem->write(IE,    0x00);

    _cycles = 0;
    _newLine = LCD_MODE_CYCLES;
    _timerCounter = 0;
    _divCounter = 0;

    _IME = 0;    //IME

    _PC = 0x0100;// Set pointer to start of rom

    // TODO: implement the Gameboy boot ROM
    // _PC = 0x0000;

    // Other / testing
    _mem->write(DIV,    0x00);
    _mem->write(LY,    0x00);
    _joypad = 0xff;
    m_halted = false;
    m_skip = false;
}

/**
 * \fn void void Cpu::Execute()
 * Called from Gameboy class and runs cycles
 * for one screen update. 70224 cycles.
**/
void Cpu::Execute()
{
    u8 opcode, cycle;

    while (_cycles < MAXCYCLES) {
        if (!m_halted) {
            if (_debuggerEnabled) {
                _dbg->debuggerUpdate(_PC);
            }
            opcode = _mem->read(_PC++);
            if (opcode != 0xcb) {
                cycle = _c[opcode];
                executeOp(opcode);
            } else {
                opcode = _mem->read(_PC++);
                cycle = _c2[opcode];
                executeOp_cb(opcode);
            }
            _log->Msg(LOG_LEVEL, LOG_CPU,
                "A: %.2x - BC: %.4x - DE: %.4x - HL: %.4x - SP: %.4x - PC: %.4x",   //  - LY: %.2x
                      _A,     _BC.w,     _DE.w,     _HL.w,       m_SP,       _PC     //,  _mem->memory[LY]
            );
            _cycles += cycle;
        } else {
            cycle = 4;
        }
        _cycles += cycle;
        updateTimer(cycle);
        updateGraphic(cycle);
        serviceInterrupts();
    }
    _cycles -= MAXCYCLES;
}

/**
 * \fn void Cpu::updateTimer(u8 cycle)
 * Called after execution of each opcode.
 * Handles the TIMA and DIV IO registers.
**/
void Cpu::updateTimer(u8 cycle)
{
    // Bit 2 - Timer Stop  (0=Stop, 1=Start)
    if (check_bit(_mem->memory[TAC],2)) { // Timer is running
        u8 tac = _mem->memory[TAC] & 0x03;
        _timerCounter += cycle;
        if (_timerCounter >= c_clockSel[tac]) {
            _timerCounter -= c_clockSel[tac];
            _mem->memory[TIMA]++;

            // Overflow happened
            if (_mem->memory[TIMA] == 0) {
                _mem->memory[TIMA] = _mem->memory[TMA];
                setInterrupt(TIMER);
            }
        }
    //} else { // TODO: Unsecure
     //   _timerCounter = 0;
     //   _mem->memory[TIMA] = _mem->memory[TMA];
    }
    // Cycles / s:  4194304
    // DIV:         incremented 16384 times / s
    //	-> every 256 cycle
    _divCounter += cycle;
    if ((_divCounter % 0xff) < cycle) {
        _mem->memory[DIV]++;
        _divCounter -= 0xff;
    }
}

/**
 * \fn void Cpu::updateGraphic(u8 cycle)
 * Called after execution of each opcode.
 * Handles the update of the LCD Screen.
 * E.g. the LCD modes.
**/
void Cpu::updateGraphic(u8 cycle)
{
    // TODO: Using the no$gmb it looks like after reset the VBlank is present for 68 cycles
    // before entering oam mode.
    u8 mode = 0;
    u8 cur_mode = (_mem->memory[STAT] & 0x03);
    u8 new_int = 0;

    // Update the LCD STATUS register
    if (!check_bit(_mem->memory[LCDC], 7))
    {
        _mem->memory[LY] = 0;
        _newLine = LCD_MODE_CYCLES;

        // When LCD screen is disabled the mode is 1
        mode = 0;
    } else {
        _newLine -= cycle;
        if (_mem->memory[LY] >= 144) {
            mode = 1;
            // Check if V-Blank interrupt is enabled
            new_int = check_bit(_mem->memory[STAT],4) ? 1 : 0;

            if (mode != cur_mode) {
                // Entering V-Blank period
                setInterrupt(V_BLANK);
            }
        } else {
            if (_newLine > LCD_MODE2_CYCLES) {
                mode = 2;
                // Check if OAM interrupt is enabled
                new_int = check_bit(_mem->memory[STAT],5) ? 1 : 0;
            } else if (_newLine > LCD_MODE3_CYCLES) {
                mode = 3;
            } else if (_newLine > LCD_MODE0_CYCLES) {
                mode = 0;
                // Check if H-Blank interrupt is enabled
                new_int = check_bit(_mem->memory[STAT], 3) ? 1 : 0;
            } else {
                mode = 2;
                _scr->DrawLine(_mem->memory[LCDC], _mem->memory[LY], _mem->memory[WY],
                _mem->memory[WX], _mem->memory[SCY], _mem->memory[SCX]);
            }
        }
        if (_newLine <= 0) {
            _newLine += LCD_MODE_CYCLES;
            _mem->memory[LY] = (_mem->memory[LY] + 1) % 153;
        }

        // new mode. set interupt
        if ((new_int == 1) && (cur_mode != mode)) {
            setInterrupt(LCD_STAT);
        }
    }
    _mem->memory[STAT] = (_mem->memory[STAT] & 0xfc) | mode | 0x80;

    // Checks if LCD is enabled
    if (check_bit(_mem->memory[LCDC], 7)) {
    }
    // Check for Coincidence Interrupt
    if (_mem->memory[LY] == _mem->memory[LYC]) {
        set_bit(&_mem->memory[STAT], 2);
        if (check_bit(_mem->memory[STAT], 6)) {
            // Set interrupt if it is enabled
            setInterrupt(LCD_STAT);
        }
    } else {
        reset_bit(&_mem->memory[STAT], 2);
    }
}

/**
 * \fn void Cpu::serviceInterrupts()
 * Called after execution of each opcode.
 * Service interrupts.
 * - Checks if IME is enabled
 * - Checks if any Interrupt flags are high (IF)
 * - Check if the interrupt is enabled (IE)
 * - Call the correct interrupt vector
**/
void Cpu::serviceInterrupts()
{
    if (_IME > 0) {
        u8 flags = _mem->memory[IF];
        if (flags > 0) {
            // looping through all the bits in IF
            for (int i = 0; i < 5; i++)
            {
                // If a interrupt flag is set
                if (check_bit(flags,i)) {
                    u8 int_ena = _mem->memory[IE];
                    // check that it is enabled
                    if (check_bit(int_ena,i)) {
                        if (m_halted) {
                            m_halted = false;
                        }
                        PUSH_nn(_PC);
                        _PC = c_interruptAddr[i];
                        // Disable other interrupts while servicing one
                        // Reenabled by the program (game)
                        _IME = 0;
                        // Interrupt is handled, reset it
                        reset_bit(&_mem->memory[IF],i);
                    }
                }
            }
        }
    }
}

/**
 * \fn void Cpu::setInterrupt(u8 bit)
 * set interrupt flag
 * \param bit   The bit# to set high 0-7
**/
void Cpu::setInterrupt(u8 bit)
{
    _mem->memory[IF] |= (1 << bit);
}

/**
 * \fn void Cpu::keyPressed(int key)
 * Key press received from Gameboy
 * \param key   Key code
**/
void Cpu::keyPressed(u8 key)
{
    _joypad &= ~key;
    if ((key > 0x0f) && (!check_bit(_mem->memory[P1], 4))) { // Direction keys
        setInterrupt(JOYPAD);
    }
     else if ((key < 0x10) && (!check_bit(_mem->memory[P1], 5))) { // Button keys
        setInterrupt(JOYPAD);
    }
    _log->Msg(DEBUG_LEVEL, LOG_CPU, "key pressed: %.2x", key);
}

/**
 * \fn void Cpu::keyReleased(int key)
 * Key released received from Gameboy
 * \param key   Key code
**/
void Cpu::keyReleased(u8 key)
{
    _joypad |= key;
    _log->Msg(DEBUG_LEVEL, LOG_CPU, "key released: %.2x", key);
}

/**
 * \fn void Cpu::executeOp(u8 opcode)
 * This function is called after reading an opcode from the rom
 * \param opcode   Current opcode to execute
**/
void Cpu::executeOp(u8 opcode)
{
    u8 n;
    u16 nn;
    switch (opcode) {
    case (0x06): LD_nn_n(&_BC.h); break;
    case (0x0e): LD_nn_n(&_BC.l); break;
    case (0x16): LD_nn_n(&_DE.h); break;
    case (0x1e): LD_nn_n(&_DE.l); break;
    case (0x26): LD_nn_n(&_HL.h); break;
    case (0x2e): LD_nn_n(&_HL.l); break;
    case (0x7f): LD_r1_r2(&_A, _A); break;
    case (0x78): LD_r1_r2(&_A, _BC.h); break;
    case (0x79): LD_r1_r2(&_A, _BC.l); break;
    case (0x7a): LD_r1_r2(&_A, _DE.h); break;
    case (0x7b): LD_r1_r2(&_A, _DE.l); break;
    case (0x7c): LD_r1_r2(&_A, _HL.h); break;
    case (0x7d): LD_r1_r2(&_A, _HL.l); break;
    case (0x7e): LD_r1_r2(&_A, _mem->read(_HL.w)); break;
    case (0x40): LD_r1_r2(&_BC.h, _BC.h); break;
    case (0x41): LD_r1_r2(&_BC.h, _BC.l); break;
    case (0x42): LD_r1_r2(&_BC.h, _DE.h); break;
    case (0x43): LD_r1_r2(&_BC.h, _DE.l); break;
    case (0x44): LD_r1_r2(&_BC.h, _HL.h); break;
    case (0x45): LD_r1_r2(&_BC.h, _HL.l); break;
    case (0x46): LD_r1_r2(&_BC.h, _mem->read(_HL.w)); break;
    case (0x48): LD_r1_r2(&_BC.l, _BC.h); break;
    case (0x49): LD_r1_r2(&_BC.l, _BC.l); break;
    case (0x4a): LD_r1_r2(&_BC.l, _DE.h); break;
    case (0x4b): LD_r1_r2(&_BC.l, _DE.l); break;
    case (0x4c): LD_r1_r2(&_BC.l, _HL.h); break;
    case (0x4d): LD_r1_r2(&_BC.l, _HL.l); break;
    case (0x4e): LD_r1_r2(&_BC.l, _mem->read(_HL.w)); break;
    case (0x50): LD_r1_r2(&_DE.h, _BC.h); break;
    case (0x51): LD_r1_r2(&_DE.h, _BC.l); break;
    case (0x52): LD_r1_r2(&_DE.h, _DE.h); break;
    case (0x53): LD_r1_r2(&_DE.h, _DE.l); break;
    case (0x54): LD_r1_r2(&_DE.h, _HL.h); break;
    case (0x55): LD_r1_r2(&_DE.h, _HL.l); break;
    case (0x56): LD_r1_r2(&_DE.h, _mem->read(_HL.w)); break;
    case (0x58): LD_r1_r2(&_DE.l, _BC.h); break;
    case (0x59): LD_r1_r2(&_DE.l, _BC.l); break;
    case (0x5a): LD_r1_r2(&_DE.l, _DE.h); break;
    case (0x5b): LD_r1_r2(&_DE.l, _DE.l); break;
    case (0x5c): LD_r1_r2(&_DE.l, _HL.h); break;
    case (0x5d): LD_r1_r2(&_DE.l, _HL.l); break;
    case (0x5e): LD_r1_r2(&_DE.l, _mem->read(_HL.w)); break;
    case (0x60): LD_r1_r2(&_HL.h, _BC.h); break;
    case (0x61): LD_r1_r2(&_HL.h, _BC.l); break;
    case (0x62): LD_r1_r2(&_HL.h, _DE.h); break;
    case (0x63): LD_r1_r2(&_HL.h, _DE.l); break;
    case (0x64): LD_r1_r2(&_HL.h, _HL.h); break;
    case (0x65): LD_r1_r2(&_HL.h, _HL.l); break;
    case (0x66): LD_r1_r2(&_HL.h, _mem->read(_HL.w)); break;
    case (0x68): LD_r1_r2(&_HL.l, _BC.h); break;
    case (0x69): LD_r1_r2(&_HL.l, _BC.l); break;
    case (0x6a): LD_r1_r2(&_HL.l, _DE.h); break;
    case (0x6b): LD_r1_r2(&_HL.l, _DE.l); break;
    case (0x6c): LD_r1_r2(&_HL.l, _HL.h); break;
    case (0x6d): LD_r1_r2(&_HL.l, _HL.l); break;
    case (0x6e): LD_r1_r2(&_HL.l, _mem->read(_HL.w)); break;
    case (0x70): LD_HL_r2(_BC.h); break;
    case (0x71): LD_HL_r2(_BC.l); break;
    case (0x72): LD_HL_r2(_DE.h); break;
    case (0x73): LD_HL_r2(_DE.l); break;
    case (0x74): LD_HL_r2(_HL.h); break;
    case (0x75): LD_HL_r2(_HL.l); break;
    case (0x36): LD_HL_r2(_mem->read(_PC++)); break;
    case (0x0a): LD_A_n(_mem->read(_BC.w)); break;
    case (0x1a): LD_A_n(_mem->read(_DE.w)); break;
    case (0xfa):
        nn = _mem->read_word(_PC);
        _PC += 2;
        LD_A_n(_mem->read(nn));
        break;
    case (0x3e): LD_A_n(_mem->read(_PC++)); break;
    case (0x47): LD_n_A(&_BC.h); break;
    case (0x4f): LD_n_A(&_BC.l); break;
    case (0x57): LD_n_A(&_DE.h); break;
    case (0x5f): LD_n_A(&_DE.l); break;
    case (0x67): LD_n_A(&_HL.h); break;
    case (0x6f): LD_n_A(&_HL.l); break;
    case (0x02): LD_nn_A(_BC.w); break;
    case (0x12): LD_nn_A(_DE.w); break;
    case (0x77): LD_nn_A(_HL.w); break;
    case (0xea):
        nn = _mem->read_word(_PC);
        _PC += 2;
        LD_nn_A(nn);
        break;
    case (0xf2): LD_A_C(); break;
    case (0xe2): LD_C_A(); break;
    case (0x3a): LDD_A_HL(); break;
    case (0x32): LDD_HL_A(); break;
    case (0x2a): LDI_A_HL(); break;
    case (0x22): LDI_HL_A(); break;
    case (0xe0): LDH_n_A(); break;
    case (0xf0): LDH_A_n(); break;
    case (0x01): LD_n_nn(&_BC.w); break;
    case (0x11): LD_n_nn(&_DE.w); break;
    case (0x21): LD_n_nn(&_HL.w); break;
    case (0x31): LD_n_nn(&m_SP); break;
    case (0xf9): LD_SP_HL(); break;
    case (0xf8): LDHL_SP_n(); break;
    case (0x08): LD_nn_SP(); break;
    case (0xf5): PUSH_nn((_A << 8) | (_Z << 7) | (_N << 6) | (_H << 5) | (_C << 4)); break;
    case (0xc5): PUSH_nn(_BC.w); break;
    case (0xd5): PUSH_nn(_DE.w); break;
    case (0xe5): PUSH_nn(_HL.w); break;
    case (0xf1):
        POP_nn(&nn);
        _A = ((nn & 0xff00) >> 8);
        _Z = ((nn & 0x0080) >> 7);
        _N = ((nn & 0x0040) >> 6);
        _H = ((nn & 0x0020) >> 5);
        _C = ((nn & 0x0010) >> 4);
        break;
    case (0xc1): POP_nn(&_BC.w); break;
    case (0xd1): POP_nn(&_DE.w); break;
    case (0xe1): POP_nn(&_HL.w); break;

    //8-Bit ALU
    case (0x87): ADD_A_n(_A); break;
    case (0x80): ADD_A_n(_BC.h); break;
    case (0x81): ADD_A_n(_BC.l); break;
    case (0x82): ADD_A_n(_DE.h); break;
    case (0x83): ADD_A_n(_DE.l); break;
    case (0x84): ADD_A_n(_HL.h); break;
    case (0x85): ADD_A_n(_HL.l); break;
    case (0x86): ADD_A_n(_mem->read(_HL.w)); break;
    case (0xc6): ADD_A_n(_mem->read(_PC++)); break;

    case (0x8f): ADC_A_n(_A); break;
    case (0x88): ADC_A_n(_BC.h); break;
    case (0x89): ADC_A_n(_BC.l); break;
    case (0x8a): ADC_A_n(_DE.h); break;
    case (0x8b): ADC_A_n(_DE.l); break;
    case (0x8c): ADC_A_n(_HL.h); break;
    case (0x8d): ADC_A_n(_HL.l); break;
    case (0x8e): ADC_A_n(_mem->read(_HL.w)); break;
    case (0xce): ADC_A_n(_mem->read(_PC++)); break;

    case (0x97): SUB_n(_A); break;
    case (0x90): SUB_n(_BC.h); break;
    case (0x91): SUB_n(_BC.l); break;
    case (0x92): SUB_n(_DE.h); break;
    case (0x93): SUB_n(_DE.l); break;
    case (0x94): SUB_n(_HL.h); break;
    case (0x95): SUB_n(_HL.l); break;
    case (0x96): SUB_n(_mem->read(_HL.w)); break;
    case (0xd6): SUB_n(_mem->read(_PC++)); break;

    case (0x9f): SBC_A_n(_A); break;
    case (0x98): SBC_A_n(_BC.h); break;
    case (0x99): SBC_A_n(_BC.l); break;
    case (0x9a): SBC_A_n(_DE.h); break;
    case (0x9b): SBC_A_n(_DE.l); break;
    case (0x9c): SBC_A_n(_HL.h); break;
    case (0x9d): SBC_A_n(_HL.l); break;
    case (0x9e): SBC_A_n(_mem->read(_HL.w)); break;
    case (0xde): SBC_A_n(_mem->read(_PC++)); break;

    case (0xa7): AND_n(_A); break;
    case (0xa0): AND_n(_BC.h); break;
    case (0xa1): AND_n(_BC.l); break;
    case (0xa2): AND_n(_DE.h); break;
    case (0xa3): AND_n(_DE.l); break;
    case (0xa4): AND_n(_HL.h); break;
    case (0xa5): AND_n(_HL.l); break;
    case (0xa6): AND_n(_mem->read(_HL.w)); break;
    case (0xe6): AND_n(_mem->read(_PC++)); break;
    case (0xb7): OR_n(_A); break;
    case (0xb0): OR_n(_BC.h); break;
    case (0xb1): OR_n(_BC.l); break;
    case (0xb2): OR_n(_DE.h); break;
    case (0xb3): OR_n(_DE.l); break;
    case (0xb4): OR_n(_HL.h); break;
    case (0xb5): OR_n(_HL.l); break;
    case (0xb6): OR_n(_mem->read(_HL.w)); break;
    case (0xf6): OR_n(_mem->read(_PC++)); break;
    case (0xaf): XOR_n(_A);break;
    case (0xa8): XOR_n(_BC.h);break;
    case (0xa9): XOR_n(_BC.l);break;
    case (0xaa): XOR_n(_DE.h);break;
    case (0xab): XOR_n(_DE.l);break;
    case (0xac): XOR_n(_HL.h);break;
    case (0xad): XOR_n(_HL.l);break;
    case (0xae): XOR_n(_mem->read(_HL.w));break;
    case (0xee): XOR_n(_mem->read(_PC++));break;

    case (0xbf): CP_n(_A);break;
    case (0xb8): CP_n(_BC.h);break;
    case (0xb9): CP_n(_BC.l);break;
    case (0xba): CP_n(_DE.h);break;
    case (0xbb): CP_n(_DE.l);break;
    case (0xbc): CP_n(_HL.h);break;
    case (0xbd): CP_n(_HL.l);break;
    case (0xbe): CP_n(_mem->read(_HL.w));break;
    case (0xfe): CP_n(_mem->read(_PC++));break;

    case (0x3c): INC_n(&_A);break;
    case (0x04): INC_n(&_BC.h);break;
    case (0x0c): INC_n(&_BC.l);break;
    case (0x14): INC_n(&_DE.h);break;
    case (0x1c): INC_n(&_DE.l);break;
    case (0x24): INC_n(&_HL.h);break;
    case (0x2c): INC_n(&_HL.l);break;
    case (0x34):
        n = _mem->read(_HL.w);
        INC_n(&n);
        _mem->write(_HL.w, n);
        break;

    case (0x3d): DEC_n(&_A);break;
    case (0x05): DEC_n(&_BC.h);break;
    case (0x0d): DEC_n(&_BC.l);break;
    case (0x15): DEC_n(&_DE.h);break;
    case (0x1d): DEC_n(&_DE.l);break;
    case (0x25): DEC_n(&_HL.h);break;
    case (0x2d): DEC_n(&_HL.l);break;
    case (0x35):
        n = _mem->read(_HL.w);
        DEC_n(&n);
        _mem->write(_HL.w, n);
        break;

    case (0x09): ADD_HL_n(&_BC.w); break;
    case (0x19): ADD_HL_n(&_DE.w); break;
    case (0x29): ADD_HL_n(&_HL.w); break;
    case (0x39): ADD_HL_n(&m_SP); break;

    case (0xe8): ADD_SP_n(); break;
    case (0x03): INC_nn(&_BC.w); break;
    case (0x13): INC_nn(&_DE.w); break;
    case (0x23): INC_nn(&_HL.w); break;
    case (0x33): INC_nn(&m_SP); break;

    case (0x0b): DEC_nn(&_BC.w);	break;
    case (0x1b): DEC_nn(&_DE.w);	break;
    case (0x2b): DEC_nn(&_HL.w);	break;
    case (0x3b): DEC_nn(&m_SP);	break;

    case (0x27): DAA();	break;
    case (0x2f): CPL();	break;
    case (0x3f): CCF(); break;
    case (0x37): SCF();	break;
    case (0x00): NOP();	break;
    case (0x76): HALT(); break;
    case (0x10):
        opcode = _mem->read(_PC++);
        switch (opcode) {
        case (0x00): STOP(); break;
            default:
                _log->Msg(WARNING_LEVEL, LOG_CPU, "Unhandled opcode (0x10): %.2x - PC: %.4X\n", opcode, _PC);
                break;
        }
        break;
    case (0xf3): DI(); break;
    case (0xfb): EI(); break;
    case (0x07): RLCA(); break;
    case (0x17): RLA();	break;
    case (0x0f): RRCA(); break;
    case (0x1f): RRA(); break;
    case (0xc3): JP_nn(); break;
    case (0xc2): JP_cc_nn(_Z, 0); break;
    case (0xca): JP_cc_nn(_Z, 1); break;
    case (0xd2): JP_cc_nn(_C, 0); break;
    case (0xda): JP_cc_nn(_C, 1); break;
    case (0xe9): JP_HL(); break;
    case (0x18): JR_n(); break;
    case (0x20): JR_cc_n(_Z, 0); break;
    case (0x28): JR_cc_n(_Z, 1); break;
    case (0x30): JR_cc_n(_C, 0); break;
    case (0x38): JR_cc_n(_C, 1); break;

    case (0xcd): CALL_nn();	break;
    case (0xc4): CALL_cc_nn(_Z, 0); break;
    case (0xcc): CALL_cc_nn(_Z, 1); break;
    case (0xd4): CALL_cc_nn(_C, 0); break;
    case (0xdc): CALL_cc_nn(_C, 1); break;
    case (0xc7): RST_n(0x0000); break;
    case (0xcf): RST_n(0x0008); break;
    case (0xd7): RST_n(0x0010); break;
    case (0xdf): RST_n(0x0018); break;
    case (0xe7): RST_n(0x0020); break;
    case (0xef): RST_n(0x0028); break;
    case (0xf7): RST_n(0x0030); break;
    case (0xff): RST_n(0x0038); break;
    case (0xc9): RET();	break;
    case (0xc0): RET_cc(_Z, 0); break;
    case (0xc8): RET_cc(_Z, 1); break;
    case (0xd0): RET_cc(_C, 0); break;
    case (0xd8): RET_cc(_C, 1); break;
    case (0xd9): RETI(); break;
    default:
        _log->Msg(WARNING_LEVEL, LOG_CPU, "Unhandled opcode: %.2x - PC: %.4X\n", opcode, _PC);
        break;
    }
    _opcode_cnt[opcode]++;
}

/**
 * \fn void Cpu::executeOp_cb(u8 opcode)
 * If the opcode is 0xcb, the next byte is read from ROM and
 * this function is executed
 * \param opcode   Current opcode to execute (0xcb)
**/
void Cpu::executeOp_cb(u8 opcode)
{
    u8 b;
    u8 n;
    switch (opcode) {
    case (0x37): SWAP_n(&_A); break;
    case (0x30): SWAP_n(&_BC.h); break;
    case (0x31): SWAP_n(&_BC.l); break;
    case (0x32): SWAP_n(&_DE.h); break;
    case (0x33): SWAP_n(&_DE.l); break;
    case (0x34): SWAP_n(&_HL.h); break;
    case (0x35): SWAP_n(&_HL.l); break;
    case (0x36):
        n = _mem->read(_HL.w);
        SWAP_n(&n);
        _mem->write(_HL.w, n);
        break;

    case (0x07): RLC_n(&_A);break;
    case (0x00): RLC_n(&_BC.h);break;
    case (0x01): RLC_n(&_BC.l);break;
    case (0x02): RLC_n(&_DE.h);break;
    case (0x03): RLC_n(&_DE.l);break;
    case (0x04): RLC_n(&_HL.h);break;
    case (0x05): RLC_n(&_HL.l);break;
    case (0x06):
        n = _mem->read(_HL.w);
        RLC_n(&n);
        _mem->write(_HL.w, n);
        break;

    case (0x17): RL_n(&_A); break;
    case (0x10): RL_n(&_BC.h); break;
    case (0x11): RL_n(&_BC.l); break;
    case (0x12): RL_n(&_DE.h); break;
    case (0x13): RL_n(&_DE.l); break;
    case (0x14): RL_n(&_HL.h); break;
    case (0x15): RL_n(&_HL.l); break;
    case (0x16):
        n = _mem->read(_HL.w);
        RL_n(&n);
        _mem->write(_HL.w, n);
        break;

    case (0x0f): RRC_n(&_A);break;
    case (0x08): RRC_n(&_BC.h);break;
    case (0x09): RRC_n(&_BC.l);break;
    case (0x0a): RRC_n(&_DE.h);break;
    case (0x0b): RRC_n(&_DE.l);break;
    case (0x0c): RRC_n(&_HL.h);break;
    case (0x0d): RRC_n(&_HL.l);break;
    case (0x0e):
        n = _mem->read(_HL.w);
        RRC_n(&n);
        _mem->write(_HL.w, n);
        break;

    case (0x1f): RR_n(&_A); break;
    case (0x18): RR_n(&_BC.h); break;
    case (0x19): RR_n(&_BC.l); break;
    case (0x1a): RR_n(&_DE.h); break;
    case (0x1b): RR_n(&_DE.l); break;
    case (0x1c): RR_n(&_HL.h); break;
    case (0x1d): RR_n(&_HL.l); break;
    case (0x1e):
        n = _mem->read(_HL.w);
        RR_n(&n);
        _mem->write(_HL.w, n);
        break;

    case (0x27): SLA_n(&_A); break;
    case (0x20): SLA_n(&_BC.h); break;
    case (0x21): SLA_n(&_BC.l); break;
    case (0x22): SLA_n(&_DE.h); break;
    case (0x23): SLA_n(&_DE.l); break;
    case (0x24): SLA_n(&_HL.h); break;
    case (0x25): SLA_n(&_HL.l); break;
    case (0x26):
        n = _mem->read(_HL.w);
        SLA_n(&n);
        _mem->write(_HL.w, n);
        break;

    case (0x2f): SRA_n(&_A); break;
    case (0x28): SRA_n(&_BC.h); break;
    case (0x29): SRA_n(&_BC.l); break;
    case (0x2a): SRA_n(&_DE.h); break;
    case (0x2b): SRA_n(&_DE.l); break;
    case (0x2c): SRA_n(&_HL.h); break;
    case (0x2d): SRA_n(&_HL.l); break;
    case (0x2e):
        n = _mem->read(_HL.w);
        SRA_n(&n);
        _mem->write(_HL.w, n);
        break;

    case (0x3f): SRL_n(&_A); break;
    case (0x38): SRL_n(&_BC.h); break;
    case (0x39): SRL_n(&_BC.l); break;
    case (0x3a): SRL_n(&_DE.h); break;
    case (0x3b): SRL_n(&_DE.l); break;
    case (0x3c): SRL_n(&_HL.h); break;
    case (0x3d): SRL_n(&_HL.l); break;
    case (0x3e):
        n = _mem->read(_HL.w);
        SRL_n(&n);
        _mem->write(_HL.w, n);
        break;

    case (0x47): BIT_b_r(0, _A); break;
    case (0x40): BIT_b_r(0, _BC.h); break;
    case (0x41): BIT_b_r(0, _BC.l); break;
    case (0x42): BIT_b_r(0, _DE.h); break;
    case (0x43): BIT_b_r(0, _DE.l); break;
    case (0x44): BIT_b_r(0, _HL.h); break;
    case (0x45): BIT_b_r(0, _HL.l); break;
    case (0x46): BIT_b_r(0, _mem->read(_HL.w)); break;
    case (0x4f): BIT_b_r(1, _A); break;
    case (0x48): BIT_b_r(1, _BC.h); break;
    case (0x49): BIT_b_r(1, _BC.l); break;
    case (0x4a): BIT_b_r(1, _DE.h); break;
    case (0x4b): BIT_b_r(1, _DE.l); break;
    case (0x4c): BIT_b_r(1, _HL.h); break;
    case (0x4d): BIT_b_r(1, _HL.l); break;
    case (0x4e): BIT_b_r(1, _mem->read(_HL.w)); break;
    case (0x57): BIT_b_r(2, _A); break;
    case (0x50): BIT_b_r(2, _BC.h); break;
    case (0x51): BIT_b_r(2, _BC.l); break;
    case (0x52): BIT_b_r(2, _DE.h); break;
    case (0x53): BIT_b_r(2, _DE.l); break;
    case (0x54): BIT_b_r(2, _HL.h); break;
    case (0x55): BIT_b_r(2, _HL.l); break;
    case (0x56): BIT_b_r(2, _mem->read(_HL.w)); break;
    case (0x5f): BIT_b_r(3, _A); break;
    case (0x58): BIT_b_r(3, _BC.h); break;
    case (0x59): BIT_b_r(3, _BC.l); break;
    case (0x5a): BIT_b_r(3, _DE.h); break;
    case (0x5b): BIT_b_r(3, _DE.l); break;
    case (0x5c): BIT_b_r(3, _HL.h); break;
    case (0x5d): BIT_b_r(3, _HL.l); break;
    case (0x5e): BIT_b_r(3, _mem->read(_HL.w)); break;
    case (0x67): BIT_b_r(4, _A); break;
    case (0x60): BIT_b_r(4, _BC.h); break;
    case (0x61): BIT_b_r(4, _BC.l); break;
    case (0x62): BIT_b_r(4, _DE.h); break;
    case (0x63): BIT_b_r(4, _DE.l); break;
    case (0x64): BIT_b_r(4, _HL.h); break;
    case (0x65): BIT_b_r(4, _HL.l); break;
    case (0x66): BIT_b_r(4, _mem->read(_HL.w)); break;
    case (0x6f): BIT_b_r(5, _A); break;
    case (0x68): BIT_b_r(5, _BC.h); break;
    case (0x69): BIT_b_r(5, _BC.l); break;
    case (0x6a): BIT_b_r(5, _DE.h); break;
    case (0x6b): BIT_b_r(5, _DE.l); break;
    case (0x6c): BIT_b_r(5, _HL.h); break;
    case (0x6d): BIT_b_r(5, _HL.l); break;
    case (0x6e): BIT_b_r(5, _mem->read(_HL.w)); break;
    case (0x77): BIT_b_r(6, _A); break;
    case (0x70): BIT_b_r(6, _BC.h); break;
    case (0x71): BIT_b_r(6, _BC.l); break;
    case (0x72): BIT_b_r(6, _DE.h); break;
    case (0x73): BIT_b_r(6, _DE.l); break;
    case (0x74): BIT_b_r(6, _HL.h); break;
    case (0x75): BIT_b_r(6, _HL.l); break;
    case (0x76): BIT_b_r(6, _mem->read(_HL.w)); break;
    case (0x7f): BIT_b_r(7, _A); break;
    case (0x78): BIT_b_r(7, _BC.h); break;
    case (0x79): BIT_b_r(7, _BC.l); break;
    case (0x7a): BIT_b_r(7, _DE.h); break;
    case (0x7b): BIT_b_r(7, _DE.l); break;
    case (0x7c): BIT_b_r(7, _HL.h); break;
    case (0x7d): BIT_b_r(7, _HL.l); break;
    case (0x7e): BIT_b_r(7, _mem->read(_HL.w)); break;

    case (0x87): RES_b_r(0, &_A); break;
    case (0x80): RES_b_r(0, &_BC.h); break;
    case (0x81): RES_b_r(0, &_BC.l); break;
    case (0x82): RES_b_r(0, &_DE.h); break;
    case (0x83): RES_b_r(0, &_DE.l); break;
    case (0x84): RES_b_r(0, &_HL.h); break;
    case (0x85): RES_b_r(0, &_HL.l); break;
    case (0x86):
        b = _mem->read(_HL.w);
        RES_b_r(0, &b);
        _mem->write(_HL.w, b);
        break;
    case (0x8f): RES_b_r(1, &_A); break;
    case (0x88): RES_b_r(1, &_BC.h); break;
    case (0x89): RES_b_r(1, &_BC.l); break;
    case (0x8a): RES_b_r(1, &_DE.h); break;
    case (0x8b): RES_b_r(1, &_DE.l); break;
    case (0x8c): RES_b_r(1, &_HL.h); break;
    case (0x8d): RES_b_r(1, &_HL.l); break;
    case (0x8e):
        b = _mem->read(_HL.w);
        RES_b_r(1, &b);
        _mem->write(_HL.w, b);
        break;
    case (0x97): RES_b_r(2, &_A); break;
    case (0x90): RES_b_r(2, &_BC.h); break;
    case (0x91): RES_b_r(2, &_BC.l); break;
    case (0x92): RES_b_r(2, &_DE.h); break;
    case (0x93): RES_b_r(2, &_DE.l); break;
    case (0x94): RES_b_r(2, &_HL.h); break;
    case (0x95): RES_b_r(2, &_HL.l); break;
    case (0x96):
        b = _mem->read(_HL.w);
        RES_b_r(2, &b);
        _mem->write(_HL.w, b);
        break;
    case (0x9f): RES_b_r(3, &_A); break;
    case (0x98): RES_b_r(3, &_BC.h); break;
    case (0x99): RES_b_r(3, &_BC.l); break;
    case (0x9a): RES_b_r(3, &_DE.h); break;
    case (0x9b): RES_b_r(3, &_DE.l); break;
    case (0x9c): RES_b_r(3, &_HL.h); break;
    case (0x9d): RES_b_r(3, &_HL.l); break;
    case (0x9e):
        b = _mem->read(_HL.w);
        RES_b_r(3, &b);
        _mem->write(_HL.w, b);
        break;
    case (0xa7): RES_b_r(4, &_A); break;
    case (0xa0): RES_b_r(4, &_BC.h); break;
    case (0xa1): RES_b_r(4, &_BC.l); break;
    case (0xa2): RES_b_r(4, &_DE.h); break;
    case (0xa3): RES_b_r(4, &_DE.l); break;
    case (0xa4): RES_b_r(4, &_HL.h); break;
    case (0xa5): RES_b_r(4, &_HL.l); break;
    case (0xa6):
        b = _mem->read(_HL.w);
        RES_b_r(4, &b);
        _mem->write(_HL.w, b);
        break;
    case (0xaf): RES_b_r(5, &_A); break;
    case (0xa8): RES_b_r(5, &_BC.h); break;
    case (0xa9): RES_b_r(5, &_BC.l); break;
    case (0xaa): RES_b_r(5, &_DE.h); break;
    case (0xab): RES_b_r(5, &_DE.l); break;
    case (0xac): RES_b_r(5, &_HL.h); break;
    case (0xad): RES_b_r(5, &_HL.l); break;
    case (0xae):
        b = _mem->read(_HL.w);
        RES_b_r(5, &b);
        _mem->write(_HL.w, b);
        break;
    case (0xb7): RES_b_r(6, &_A); break;
    case (0xb0): RES_b_r(6, &_BC.h); break;
    case (0xb1): RES_b_r(6, &_BC.l); break;
    case (0xb2): RES_b_r(6, &_DE.h); break;
    case (0xb3): RES_b_r(6, &_DE.l); break;
    case (0xb4): RES_b_r(6, &_HL.h); break;
    case (0xb5): RES_b_r(6, &_HL.l); break;
    case (0xb6):
        b = _mem->read(_HL.w);
        RES_b_r(6, &b);
        _mem->write(_HL.w, b);
        break;
    case (0xbf): RES_b_r(7, &_A); break;
    case (0xb8): RES_b_r(7, &_BC.h); break;
    case (0xb9): RES_b_r(7, &_BC.l); break;
    case (0xba): RES_b_r(7, &_DE.h); break;
    case (0xbb): RES_b_r(7, &_DE.l); break;
    case (0xbc): RES_b_r(7, &_HL.h); break;
    case (0xbd): RES_b_r(7, &_HL.l); break;
    case (0xbe):
        b = _mem->read(_HL.w);
        RES_b_r(7, &b);
        _mem->write(_HL.w, b);
        break;

    case (0xc7): SET_b_r(0, &_A); break;
    case (0xc0): SET_b_r(0, &_BC.h); break;
    case (0xc1): SET_b_r(0, &_BC.l); break;
    case (0xc2): SET_b_r(0, &_DE.h); break;
    case (0xc3): SET_b_r(0, &_DE.l); break;
    case (0xc4): SET_b_r(0, &_HL.h); break;
    case (0xc5): SET_b_r(0, &_HL.l); break;
    case (0xc6):
        b = _mem->read(_HL.w);
        SET_b_r(0, &b);
        _mem->write(_HL.w, b);
        break;
    case (0xcf): SET_b_r(1, &_A); break;
    case (0xc8): SET_b_r(1, &_BC.h); break;
    case (0xc9): SET_b_r(1, &_BC.l); break;
    case (0xca): SET_b_r(1, &_DE.h); break;
    case (0xcb): SET_b_r(1, &_DE.l); break;
    case (0xcc): SET_b_r(1, &_HL.h); break;
    case (0xcd): SET_b_r(1, &_HL.l); break;
    case (0xce):
        b = _mem->read(_HL.w);
        SET_b_r(1, &b);
        _mem->write(_HL.w, b);
        break;
    case (0xd7): SET_b_r(2, &_A); break;
    case (0xd0): SET_b_r(2, &_BC.h); break;
    case (0xd1): SET_b_r(2, &_BC.l); break;
    case (0xd2): SET_b_r(2, &_DE.h); break;
    case (0xd3): SET_b_r(2, &_DE.l); break;
    case (0xd4): SET_b_r(2, &_HL.h); break;
    case (0xd5): SET_b_r(2, &_HL.l); break;
    case (0xd6):
        b = _mem->read(_HL.w);
        SET_b_r(2, &b);
        _mem->write(_HL.w, b);
        break;
    case (0xdf): SET_b_r(3, &_A); break;
    case (0xd8): SET_b_r(3, &_BC.h); break;
    case (0xd9): SET_b_r(3, &_BC.l); break;
    case (0xda): SET_b_r(3, &_DE.h); break;
    case (0xdb): SET_b_r(3, &_DE.l); break;
    case (0xdc): SET_b_r(3, &_HL.h); break;
    case (0xdd): SET_b_r(3, &_HL.l); break;
    case (0xde):
        b = _mem->read(_HL.w);
        SET_b_r(3, &b);
        _mem->write(_HL.w, b);
        break;
    case (0xe7): SET_b_r(4, &_A); break;
    case (0xe0): SET_b_r(4, &_BC.h); break;
    case (0xe1): SET_b_r(4, &_BC.l); break;
    case (0xe2): SET_b_r(4, &_DE.h); break;
    case (0xe3): SET_b_r(4, &_DE.l); break;
    case (0xe4): SET_b_r(4, &_HL.h); break;
    case (0xe5): SET_b_r(4, &_HL.l); break;
    case (0xe6):
        b = _mem->read(_HL.w);
        SET_b_r(4, &b);
        _mem->write(_HL.w, b);
        break;
    case (0xef): SET_b_r(5, &_A); break;
    case (0xe8): SET_b_r(5, &_BC.h); break;
    case (0xe9): SET_b_r(5, &_BC.l); break;
    case (0xea): SET_b_r(5, &_DE.h); break;
    case (0xeb): SET_b_r(5, &_DE.l); break;
    case (0xec): SET_b_r(5, &_HL.h); break;
    case (0xed): SET_b_r(5, &_HL.l); break;
    case (0xee):
        b = _mem->read(_HL.w);
        SET_b_r(5, &b);
        _mem->write(_HL.w, b);
        break;
    case (0xf7): SET_b_r(6, &_A); break;
    case (0xf0): SET_b_r(6, &_BC.h); break;
    case (0xf1): SET_b_r(6, &_BC.l); break;
    case (0xf2): SET_b_r(6, &_DE.h); break;
    case (0xf3): SET_b_r(6, &_DE.l); break;
    case (0xf4): SET_b_r(6, &_HL.h); break;
    case (0xf5): SET_b_r(6, &_HL.l); break;
    case (0xf6):
        b = _mem->read(_HL.w);
        SET_b_r(6, &b);
        _mem->write(_HL.w, b);
        break;
    case (0xff): SET_b_r(7, &_A); break;
    case (0xf8): SET_b_r(7, &_BC.h); break;
    case (0xf9): SET_b_r(7, &_BC.l); break;
    case (0xfa): SET_b_r(7, &_DE.h); break;
    case (0xfb): SET_b_r(7, &_DE.l); break;
    case (0xfc): SET_b_r(7, &_HL.h); break;
    case (0xfd): SET_b_r(7, &_HL.l); break;
    case (0xfe):
        b = _mem->read(_HL.w);
        SET_b_r(7, &b);
        _mem->write(_HL.w, b);
        break;

    default:
        _log->Msg(WARNING_LEVEL, LOG_CPU, "Unhandled opcode (0xcb): %.2x - PC: %.4X\n", opcode, _PC);
        break;
    }
    _opcode_cnt_2[opcode]++;
}

/** Name: LD nn,n
Description: Put value nn into n.
Use with:
    nn = B,C,D,E,H,L,BC,DE,HL,SP
    n = 8 bit immediate value **/
void Cpu::LD_nn_n(u8 *nn)
{
    *nn = _mem->read(_PC++);
}

/** Name: LD r1,r2
Description: Put value r2 into r1.
Use with: r1,r2 = A,B,C,D,E,H,L,(HL) **/
void Cpu::LD_r1_r2(u8 *r1, u8 r2)
{
    *r1 = r2;
}
void Cpu::LD_HL_r2(u8 r2)
{
    _mem->write(_HL.w, r2);
}

/** Name: LD A,n
Description: Put value n into A.
Use with:
    n = A,B,C,D,E,H,L,(BC),(DE),(HL),(nn),#
    nn = two byte immediate value. (LS byte first.) **/
void Cpu::LD_A_n(u8 n)
{
    _A = n;
}

/** Name: LD n,_A
Description: Put value _A into n.
Use with:
    n = _A,B,C,D,E,H,L,(BC),(DE),(HL),(nn)
    nn = two byte immediate value. (LS byte first.) **/
void Cpu::LD_n_A(u8 *n)
{
    *n = _A;
}
void Cpu::LD_nn_A(u16 nn)
{
    _mem->write(nn, _A);
}

/** Name: LD A,(C)
Description: Put value at address $FF00 + register C into A. Same as: LD A,($FF00+C) **/
void Cpu::LD_A_C()
{
    _A = _mem->read(0xff00 + _BC.l);
}

/** Name: LD (C),A
Description: Put A into address $FF00 + register C. **/
void Cpu::LD_C_A()
{
    _mem->write(0xff00 + _BC.l, _A);
}

/** Name: LDD A,(HL)
Description: Put value at address HL into A. Decrement HL. Same as: LD A,(HL) - DEC HL **/
void Cpu::LDD_A_HL()
{
    _A = _mem->read(_HL.w--);
}

/** Name: LDD (HL),A
Description: Put A into _mem address HL. Decrement HL. Same as: LD (HL),A - DEC HL **/
void Cpu::LDD_HL_A()
{
    _mem->write(_HL.w--, _A);
}

/** Name: LDI A,(HL)
Description: Put value at address HL into A. Increment HL. Same as: LD A,(HL) - INC HL **/
void Cpu::LDI_A_HL()
{
    _A = _mem->read(_HL.w++);
}

/** Name: LDI (HL),A
Description: Put A into _mem address HL. Increment HL. Same as: LD (HL),A - INC HL **/
void Cpu::LDI_HL_A()
{
    _mem->write(_HL.w++, _A);
}

/** Name: LDH (n),A
Description: Put A into _mem address $FF00+n.
Use with: n = one byte immediate value. **/
void Cpu::LDH_n_A()
{
    u16 addr = 0xff00 + _mem->read(_PC++);
    _mem->write(addr, _A);
}

/** Name: LDH A,(n)
Description: Put _mem address $FF00+n into A.
Use with: n = one byte immediate value. **/
void Cpu::LDH_A_n()
{
    u16 addr = 0xff00 + _mem->read(_PC++);
    _A = _mem->read(addr);
}

// 16-Bit Loads
/** Name: LD n,nn
Description: Put value nn into n.
Use with:
    n = BC,DE,HL,SP
    nn = 16 bit immediate value **/
void Cpu::LD_n_nn(u16 *n)
{
    *n = _mem->read_word(_PC);
    _PC += 2;
}

/** Name: LD SP,HL
Description: Put HL into Stack Pointer (SP). **/
void Cpu::LD_SP_HL()
{
    m_SP = _HL.w;
}

/** Name: LDHL SP,n
Description: Put SP + n effective address into HL.
Use with: n = one byte signed immediate value.
Flags affected:
    Z - Reset.
    N - Reset.
    H - Set or reset according to operation.
    C - Set or reset according to operation. **/
void Cpu::LDHL_SP_n()
{
    s8 s = _mem->read(_PC++);
    _Z = 0;
    _N = 0;
    _H = (((m_SP & 0x0fff) + s) > 0x0fff) ? 1 : 0;
    _C = ((m_SP + s) > 0xffff) ? 1 : 0;
    _HL.w = m_SP + s;
}

/**	Name: LD (nn),SP
Description: Put Stack Pointer (SP) at address n.
Use with: nn = two byte immediate address. **/
void Cpu::LD_nn_SP()
{
    u16 nn = _mem->read_word(_PC);
    _PC += 2;
    _mem->write_word(nn, m_SP);
}

/** Name: PUSH nn
Description: Push register pair nn onto stack. Decrement Stack Pointer (SP) twice.
Use with: nn = AF,BC,DE,HL **/
void Cpu::PUSH_nn(u16 nn)
{
    m_SP -= 2;
    _mem->write_word(m_SP, nn);
}


/**	Name: POP nn
Description: Pop two bytes off stack into register pair nn. Increment Stack Pointer (SP) twice.
Use with: nn = AF,BC,DE,HL **/
void Cpu::POP_nn(u16 *nn)
{
    *nn = _mem->read_word(m_SP);
    m_SP += 2;
}

//8-Bit ALU
/** Name: ADD A,n
Description: Add n to A.
Use with: n = A,B,C,D,E,H,L,(HL),#
Flags affected:
    Z - Set if result is zero.
    N - Reset.
    H - Set if carry from bit 3.
    C - Set if carry from bit 7. **/
void Cpu::ADD_A_n(u8 n)
{
    _N = 0;
    _C = (((_A + n) > 0xff)) ? 1 : 0;
    _H = (((_A & 0x0f) + (n & 0x0f)) > 0x0f) ? 1 : 0;
    _A += n;
    _Z = (_A == 0) ? 1 : 0;
}

/** Name: ADC A,n
Description: Add n + Carry flag to A.
Use with: n = A,B,C,D,E,H,L,(HL),#
Flags affected:
    Z - Set if result is zero.
    N - Reset.
    H - Set if carry from bit 3.
    C - Set if carry from bit 7. **/
void Cpu::ADC_A_n(u8 n)
{
    u8 tmp_C = _C;
    _N = 0;
    _C = (((_A + n + tmp_C) > 0xff)) ? 1 : 0;
    _H = (((_A & 0x0f) + (n & 0x0f) + tmp_C) > 0x0f) ? 1 : 0;
    _A = _A + n + tmp_C;
    _Z = (_A == 0) ? 1 : 0;
}

/** Name: SUB n
Description: Subtract n from A.
Use with: n = A,B,C,D,E,H,L,(HL),#
Flags affected:
    Z - Set if result is zero.
    N - Set.
    H - Set if no borrow from bit 4.
    C - Set if no borrow. **/
void Cpu::SUB_n(u8 n)
{
    _N = 1;
    _C = (_A < n) ? 1 : 0;
    _H = ((_A & 0x0f) < (n & 0x0f)) ? 1 : 0;
    _A -= n;
    _Z = (_A == 0) ? 1 : 0;
}

/** Name: SBC A,n
Description: Subtract n + Carry flag from A.
Use with: n = A,B,C,D,E,H,L,(HL),#
Flags affected:
    Z - Set if result is zero.
    N - Set.
    H - Set if no borrow from bit 4.
    C - Set if no borrow. **/
void Cpu::SBC_A_n(u8 n)
{
    u8 tmp_C = _C;
    _N = 1;
    _C = (_A < (n + tmp_C)) ? 1 : 0;
    _H = ((_A & 0xf0) < ((n + tmp_C) & 0xf0)) ? 1 : 0;
    _A = _A - n - tmp_C;
    _Z = (_A == 0) ? 1 : 0;
}

/** Name: AND n
Description: Logically AND n with A, result in A.
Use with: n = A,B,C,D,E,H,L,(HL),#
Flags affected:
    Z - Set if result is zero.
    N - Reset.
    H - Set.
    C - Reset. **/
void Cpu::AND_n(u8 n)
{
    _A &= n;
    _N = 0;
    _C = 0;
    _H = 1;
    _Z = (_A == 0) ? 1 : 0;
}

/** Name: OR n
Description: Logical OR n with register A, result in A.
Use with: n = A,B,C,D,E,H,L,(HL),#
Flags affected:
    Z - Set if result is zero.
    N - Reset.
    H - Reset.
    C - Reset. **/
void Cpu::OR_n(u8 n)
{
    _A |= n;
    _N = 0;
    _C = 0;
    _H = 0;
    _Z = (_A == 0) ? 1 : 0;
}

/** Name: XOR n
Description: Logical exclusive OR n with register A, result in A.
Use with: n = A,B,C,D,E,H,L,(HL),#
Flags affected:
    Z - Set if result is zero.
    N - Reset.
    H - Reset.
    C - Reset. **/
void Cpu::XOR_n(u8 n)
{
    _A ^= n;
    _N = 0;
    _C = 0;
    _H = 0;
    _Z = (_A == 0) ? 1 : 0;
}

/** Name: CP n
Description: Compare A with n. This is basically an A - n
    subtraction instruction but the results are thrown away.
Use with: n = A,B,C,D,E,H,L,(HL),#
Flags affected:
    Z - Set if result is zero. (Set if A = n.)
    N - Set.
    H - Set if no borrow from bit 4.
    C - Set for no borrow. (Set if A < n.) **/
void Cpu::CP_n(u8 n)
{
    _Z = (_A == n) ? 1 : 0;
    _N = 1;
    _H = ((_A & 0x0f) < (n & 0x0f)) ? 1 : 0;
    _C = (_A < n) ? 1 : 0;
}

/** Name: INC n
Description: Increment register n.
Use with: n = A,B,C,D,E,H,L,(HL)
Flags affected:
    Z - Set if result is zero.
    N - Reset.
    H - Set if carry from bit 3.
    C - Not affected. **/
void Cpu::INC_n(u8 *n)
{
    _N = 0;
    _C = (*n == 0xff) ? 1 : 0;
    _H = ((*n & 0x0f) == 0x0f) ? 1 : 0;
    *n = *n + 1;
    _Z = (*n == 0) ? 1 : 0;
}

/** Name: DEC n
Description: Decrement register n.
Use with: n = A,B,C,D,E,H,L,(HL)
Flags affected:
    Z - Set if reselt is zero.
    N - Set.
    H - Set if no borrow from bit 4.
    C - Not affected. **/
void Cpu::DEC_n(u8 *n)
{
    _N = 1;
    *n = *n - 1;
    _H = ((*n & 0x0f) == 0x0f) ? 1 : 0;
    _Z = (*n == 0) ? 1 : 0;
}

// 16-Bit Arithmetic
/** Name: ADD HL,n
Description: Add n to HL.
Use with: n = BC,DE,HL,SP
Flags affected:
    Z - Not affected.
    N - Reset.
    H - Set if carry from bit 11.
    C - Set if carry from bit 15. **/
void Cpu::ADD_HL_n(u16 *n)
{
    _N = 0;
    _H = (((_HL.w & 0x0fff) + (*n & 0x0fff)) > 0x0fff) ? 1 : 0;
    _C = ((_HL.w + *n) > 0xffff) ? 1 : 0;
    _HL.w = _HL.w + *n;
}

/** Name: ADD SP,n
Description: Add n to Stack Pointer (SP)
Use with: n = one byte signed immediate value (#).
Flags affected:
    Z - Reset.
    N - Reset.
    H - Set or reset according to operation.
    C - Set or reset according to operation. **/
void Cpu::ADD_SP_n()
{
    _Z = 0;
    _N = 0;
    s8 s = _mem->read(_PC++);
    _H = (((m_SP & 0x0fff) + s) > 0x0fff) ? 1 : 0;
    _C = ((m_SP + s) > 0xffff) ? 1 : 0;
    m_SP += s;
}

/** Name: INC nn
Description: Increment register nn.
Use with: nn = BC,DE,HL,SP **/
void Cpu::INC_nn(u16 *nn)
{
    *nn = *nn + 1;
}

/** Name: DEC nn
Description: Decrement register nn.
Use with: nn = BC,DE,HL,SP **/
void Cpu::DEC_nn(u16 *nn)
{
    *nn = *nn - 1;
}

// Miscellaneous
/** Name: SWAP n
Description: Swap upper & lower nibles of n.
Use with: n = A,B,C,D,E,H,L,(HL)
Flags affected:
    Z - Set if result is zero.
    N - Reset.
    H - Reset.
    C - Reset. **/
void Cpu::SWAP_n(u8 *n)
{
    *n = ((*n & 0x0f) << 4) | ((*n & 0xf0) >> 4);
    _Z = (*n == 0) ? 1 : 0;
    _N = 0;
    _H = 0;
    _C = 0;
}

/**
Name: DAA
Description:
    Decimal adjust register A. This instruction adjusts register A so that the
    correct representation of Binary Coded Decimal (BCD) is obtained.
Flags affected:
    Z - Set if register A is zero.
    N - Not affected.
    H - Reset.
    C - Set or reset according to operation.
**/
void Cpu::DAA()
{
    u8 tmp = 0;
    u8 nl = _A & 0x0f;
    u8 nh = (_A & 0xf0) >> 4;

    if (_N == 0) { // Indicates the last instruction was an addition
        if ((_H == 1) || (nl > 0x09)) {
            tmp += 0x06;
        }
        if ((_C == 1) || (nh > 0x09) || ((nh == 9) && (nl > 9))) {
            tmp += 0x60;
        }
        if (tmp > 0x5f) {
            _C = 1;
        } else {
            _C = 0;
        }
    } else {	// Indicates last instruction was a subtraction
        if ((_C == 0) && (_H == 1)) {
            tmp += 0xfa;
            _C = 0;
        } else if ((_C == 1) && (_H == 0)) {
            tmp += 0xa0;
            _C = 1;
        } else if ((_C == 1) && (_H == 1)) {
            tmp += 0x9a;
            _C = 1;
        } else {
            _C = 0;
        }
    }
    if (tmp == 0x00) {
        _Z = 1;
    } else {
        _Z = 0;
    }
    _H = 0;
    _A -= tmp;
}

/** Name: CPL
Description: Complement A register. (Flip all bits.)
Flags affected:
    Z - Not affected.
    N - Set.
    H - Set.
    C - Not affected. **/
void Cpu::CPL()
{
    _A = ~_A;
    _N = 1;
    _H = 1;
}

/**	Name: CCF
Description: Complement carry flag.	If C flag is set, then reset it. If C flag is reset, then set it.
Flags affected:
        Z - Not affected.
        N - Reset.
        H - Reset.
        C - Complemented. **/
void Cpu::CCF()
{
    _C = (_C == 0) ? 1 : 0;
    _N = 0;
    _H = 0;
}

/** Name: SCF
Description: Set Carry flag.
Flags affected:
        Z - Not affected.
        N - Reset.
        H - Reset.
        C - Set. **/
void Cpu::SCF()
{
    _N = 0;
    _H = 0;
    _C = 1;
}

/**	Name: NOP
Description: No Operation. **/
void Cpu::NOP()
{
}

/** Name: HALT
Description: Power down CPU until an interrupt occurs. Use this when ever possible to reduce energy consumption. **/
void Cpu::HALT()
{
    if (_IME > 0) {
        m_halted = true;
    } else {
        // If interrupt is disabled the opcode following the halt is repeated once.
        // TODO: That means after reading the opcode, _PC should be decremented.
        m_skip = true;
    }
}

/** Name: STOP
Description: Halt CPU & LCD display until button pressed. **/
void Cpu::STOP()
{
    // TODO
    _log->Msg(DEBUG_LEVEL, LOG_CPU, "STOP() not implemented");
}

/**	Name: DI
Description:
        This instruction disables interrupts but not immediately. Interrupts are disabled after
        instruction after DI is executed.
Flags affected:	None. **/
void Cpu::DI()
{
    _IME = 0;
    // TODO: Delay the disable 1 instruction.
}

/**	Name: EI
Description:
        Enable interrupts. This intruction enables interrupts
        but not immediately. Interrupts are enabled after
        instruction after EI is executed.
Flags affected:	None. **/
void Cpu::EI()
{
    _IME = 1;
    // TODO: Delay the disable 1 instruction.
}

// Rotates & Shifts
/** Name: RLCA
Description: Rotate A left. Old bit 7 to Carry flag.
Flags affected:
    Z - Set if result is zero.
    N - Reset.
    H - Reset.
    C - Contains old bit 7 data. **/
void Cpu::RLCA()
{
    RLC_n(&_A);
}

/** Name: RLA
Description: Rotate A left through Carry flag.
Flags affected:
    Z - Set if result is zero.
    N - Reset.
    H - Reset.
    C - Contains old bit 7 data. **/
void Cpu::RLA()
{
    RL_n(&_A);
}

/** Name: RRCA
Description: Rotate A right. Old bit 0 to Carry flag.
Flags affected:
    Z - Set if result is zero.
    N - Reset.
    H - Reset.
    C - Contains old bit 0 data. **/
void Cpu::RRCA()
{
    RRC_n(&_A);
}

/* Name: RRA
Description: Rotate A right through Carry flag.
Flags affected:
    Z - Set if result is zero.
    N - Reset.
    H - Reset.
    C - Contains old bit 0 data. **/
void Cpu::RRA()
{
    RR_n(&_A);
}

/** Name: RLC n
Description: Rotate n left. Old bit 7 to Carry flag.
Use with: n = A,B,C,D,E,H,L,(HL)
Flags affected:
    Z - Set if result is zero.
    N - Reset.
    H - Reset.
    C - Contains old bit 7 data. **/
void Cpu::RLC_n(u8 *n)
{
    _C = (*n > 0x7f) ? 1 : 0;
    *n = *n << 1;
    *n = *n | _C; // Copy MSb to LSb
    _H = 0;
    _N = 0;
    _Z = (*n == 0) ? 1 : 0;
}

/** Name: RL n
Description: Rotate n left through Carry flag.
Use with: n = A,B,C,D,E,H,L,(HL)
Flags affected:
    Z - Set if result is zero.
    N - Reset.
    H - Reset.
    C - Contains old bit 7 data. **/
void Cpu::RL_n(u8 *n)
{
    u8 tmp_C = _C;
    _C = (*n > 0x7f) ? 1 : 0;
    *n = *n << 1;
    *n = *n | tmp_C;
    _N = 0;
    _H = 0;
    _Z = (*n == 0) ? 1 : 0;
}

/** Name: RRC n
Description: Rotate n right. Old bit 0 to Carry flag.
Use with: n = A,B,C,D,E,H,L,(HL)
Flags affected:
    Z - Set if result is zero.
    N - Reset.
    H - Reset.
    C - Contains old bit 0 data. **/
void Cpu::RRC_n(u8 *n)
{
    _C = ((*n & 0x01) == 1) ? 1 : 0;
    *n = *n >> 1;
    *n = *n | (_C << 7);    // Copy LSb to MSb
    _Z = (*n == 0) ? 1 : 0;
    _N = 0;
    _H = 0;
}

/**
Name: RR n
Description: Rotate n right through Carry flag.
Use with: n = A,B,C,D,E,H,L,(HL)
Flags affected:
    Z - Set if result is zero.
    N - Reset.
    H - Reset.
    C - Contains old bit 0 data. **/
void Cpu::RR_n(u8 *n)
{
    u8 tmp_C = _C;
    _C = ((*n & 0x01) == 1) ? 1 : 0;
    *n = *n >> 1;
    *n = *n | (tmp_C << 7);	// Copy MSb to LSb
    _Z = (*n == 0) ? 1 : 0;
    _N = 0;
    _H = 0;
}

/** Name: SLA n
Description: Shift n left into Carry. LSB of n set to 0.
Use with: n = A,B,C,D,E,H,L,(HL)
Flags affected:
    Z - Set if result is zero.
    N - Reset.
    H - Reset.
    C - Contains old bit 7 data. **/
void Cpu::SLA_n(u8 *n)
{
    _C = (*n > 0x7f) ? 1 : 0;
    *n = *n << 1;
    _N = 0;
    _H = 0;
    _Z = (*n == 0) ? 1 : 0;
}

/** Name: SRA n
Description: Shift n right into Carry. MSB doesn't change.
Use with: n = A,B,C,D,E,H,L,(HL)
Flags affected:
    Z - Set if result is zero.
    N - Reset.
    H - Reset.
    C - Contains old bit 0 data. **/
void Cpu::SRA_n(u8 *n)
{
    _C = ((*n & 0x01) == 1) ? 1 : 0;
    *n = *n >> 1;
    // MSb doesn't change
    if ((*n & 0x40) == 0x40) {
        set_bit(n, 7);
    }
    _Z = (*n == 0) ? 1 : 0;
    _N = 0;
    _H = 0;
}

/** Name: SRL n
Description: Shift n right into Carry. MSB set to 0.
Use with: n = A,B,C,D,E,H,L,(HL)
Flags affected:
        Z - Set if result is zero.
        N - Reset.
        H - Reset.
        C - Contains old bit 0 data. **/
void Cpu::SRL_n(u8 *n)
{
    _C = ((*n & 0x01) == 1) ? 1 : 0;
    *n = *n >> 1;
    _N = 0;
    _H = 0;
    _Z = (*n == 0) ? 1 : 0;
}

// Bit Opcodes
/** Name: BIT b,r
Description: Test bit b in register r.
Use with: b = 0 - 7, r = A,B,C,D,E,H,L,(HL)
Flags affected:
    Z - Set if bit b of register r is 0.
    N - Reset.
    H - Set.
    C - Not affected. **/
void Cpu::BIT_b_r(u8 b, u8 r)
{
    _N = 0;
    _H = 1;
    _Z = (check_bit(r, b)) ? 0 : 1;
}

/** Name: SET b,r
Description: Set bit b in register r.
Use with: b = 0 - 7, r = A,B,C,D,E,H,L,(HL)
Flags affected: None. **/
void Cpu::SET_b_r(u8 b, u8 *r)
{
    set_bit(r, b);
}

/** Name: RES b,r
Description: Reset bit b in register r.
Use with: b = 0 - 7, r = A,B,C,D,E,H,L,(HL) **/
void Cpu::RES_b_r(u8 b, u8 *r)
{
    reset_bit(r, b);
}

// Jumps
/** Name: JP nn
Description: Jump to address nn.
Use with: nn = two byte immediate value. (LS byte first.)
Flags affected:	None. **/
void Cpu::JP_nn()
{
    _PC = _mem->read_word(_PC);
}

/**
Name: JP cc,nn
Description:
        Jump to address n if following condition is true:
        cc = NZ, Jump if Z flag is reset.
        cc = Z, Jump if Z flag is set.
        cc = NC, Jump if C flag is reset.
        cc = C, Jump if C flag is set.
Use with:
    nn = two byte immediate value. (LS byte first.)
Flags affected:
        None.
**/
void Cpu::JP_cc_nn(u8 flag, u8 test)
{
    u16 nn = _mem->read_word(_PC);
    _PC += 2;
    if (flag == test) {
        _PC = nn;
    }
}

/** Name: JP (HL)
Description: Jump to address contained in HL. **/
void Cpu::JP_HL()
{
    _PC = _HL.w;
}

/** Name: JR n
Description: Add n to current address and jump to it.
Use with: n = one byte signed immediate value
**/
void Cpu::JR_n()
{
    s8 s = _mem->read(_PC++);
    _PC += s;
}

/** Name: JR cc n
Description: If following condition is true then add n to current address and jump to it:
Use with:
        n = one byte signed immediate value
        cc = NZ, Jump if Z flag is reset.
        cc = Z, Jump if Z flag is set.
        cc = NC, Jump if C flag is reset.
        cc = C, Jump if C flag is set. **/
void Cpu::JR_cc_n(u8 flag, u8 test)
{
    s8 s = _mem->read(_PC++);
    if (flag == test) {
        _PC += s;
    }
}

// Calls
/** Name: CALL nn
Description: Push address of next instruction onto stack and then jump to address nn.
Use with: nn = two byte immediate value. (LS byte first.) **/
void Cpu::CALL_nn()
{
    u16 nn;
    nn = _mem->read_word(_PC);
    _PC += 2;
    PUSH_nn(_PC);
    _PC = nn;
}

/** Name: CALL cc,nn
Description:
        Call address n if following condition is true:
        cc = NZ, Call if Z flag is reset.
        cc = Z, Call if Z flag is set.
        cc = NC, Call if C flag is reset.
        cc = C, Call if C flag is set.
Use with: nn = two byte immediate value. (LS byte first.) **/
void Cpu::CALL_cc_nn(u8 flag, u8 test)
{
    u16 nn = _mem->read_word(_PC);
    _PC += 2;
    if (flag == test) {
        PUSH_nn(_PC);
        _PC = nn;
    }
}

// Restarts
/** Name: RST n
Description: Push present address onto stack. Jump to address $0000 + n.
Use with: n = $00,$08,$10,$18,$20,$28,$30,$38
**/
void Cpu::RST_n(u16 addr)
{
    PUSH_nn(_PC);
    _PC = addr;
}

// Returns
/** Name: RET
Description: Pop two bytes from stack & jump to that address. **/
void Cpu::RET()
{
    POP_nn(&_PC);
}

/** Name: RET cc
Description: Return if following condition is true:
Use with:
    cc = NZ, Return if Z flag is reset.
    cc = Z, Return if Z flag is set.
    cc = NC, Return if C flag is reset.
    cc = C, Return if C flag is set. **/
void Cpu::RET_cc(u8 flag, u8 test)
{
    if (flag == test) {
        POP_nn(&_PC);
    }
}

/** Name: RETI
Description: Pop two bytes from stack & jump to that address then enable interrupts. **/
void Cpu::RETI()
{
    POP_nn(&_PC);
    _IME = 1;
}

